package com.qsw.datetimepickerlib.DateTimePicker.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.animation.DecelerateInterpolator;

import com.qsw.datetimepickerlib.view.DateTimePicker.bean.ItemBean;

import java.util.ArrayList;
import java.util.List;

/**
 * 仿NumberPicker控件
 */
public class WheelView extends View {
    private static final String TAG = "TAG_WheelView";
    /**
     * 左右空白间距
     */
    private static final int PADDINT_LEFTRIGHT = 50;
    /**
     * 惯性滑动的时间单位-微秒
     */
    private static final int SCROLL_INTERVAL_TIME = 1000;
    /**
     * 上下两条线距离中间文字的距离
     */
    private static final int INTERVAL_LINE_TO_TEXT = 14;
    /**
     * 默认未选中的文字大小
     */
    private static final int DEFAULT_TEXT_SIZE = 17;
    /**
     * 默认当前选中的文字大小
     */
    private static final int DEFAULT_TEXT_SELECT_SIZE = DEFAULT_TEXT_SIZE + 1;
    /**
     * 默认未选中的文字颜色
     */
    private static final int DEFAULT_TEXT_COLOR = Color.BLACK;
    /**
     * 默认当前选中的文字颜色
     */
    private static final int DEFAULT_TEXT_SELECT_COLOR = Color.RED;
    /**
     * 默认上下两条线的颜色
     */
    private static final int DEFAULT_LINE_COLOR = Color.BLACK;
    /**
     * 当前选择条目上方最多显示出该数量的待选条目
     */
    private static final int DEFAULT_ITEM_NUM_UP = 3;
    /**
     * 当前选择条目下方最多显示出该数量的待选条目
     */
    private static final int DEFAULT_ITEM_NUM_DOWN = DEFAULT_ITEM_NUM_UP;

    private Paint mPaint;

    private List<String> mStrList = new ArrayList<>();
    private List<ItemBean> mItemList = new ArrayList<>();
    private boolean mCanInitData;

    private int mDivideLineToTextSpacePx;

    private float mLastY;
    private float mSpeed;
    private float mBaseLineY;
    private int mLastSelectedIndex;
    private int mSelectedIndex;
    private int mNeedSetSelectedIndex = -1;
    private boolean mSpeedRequestZero;
    private PointF mTouchDownPoint = null;

    private ScrollThread mScrollThread;
    private DecelerateInterpolator mInterpolator = new DecelerateInterpolator(1);
    private OnItemChangedListener mOnItemChangedListener;

    private int mMinViewWidth;          // 控件最小宽度
    private int mTextSizePx;            // 字体尺寸
    private int mSelectedTextSizePx;   // 当前选择的字体尺寸
    private int mTextColor;             // 字体颜色
    private int mSelectedTextColor;    // 当前选择的字体颜色
    private int mLineColor;             // 上下两条分割线颜色
    private int mUpItemCount;           // 上方的备选条目数
    private int mDownItemCount;         // 下方的备选条目数
    private int mUpShowSpace;           // 上方显示空间
    private int mDownShowSpace;         // 下方显示空间
    private int mItemDistance;          // 条目间的最大间距，即当前所选条目下边缘与上下条目下边缘之间的间距
    private int mMaxTextWidth;          // 条目中文字的最大宽度

    public WheelView(Context context) {
        this(context, null);
    }

    public WheelView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WheelView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        init();
    }

    private void init() {
        mTextSizePx = DTPLUtil.dp2px(getContext(), DEFAULT_TEXT_SIZE);
        mSelectedTextSizePx = DTPLUtil.dp2px(getContext(), DEFAULT_TEXT_SELECT_SIZE);
        mTextColor = DEFAULT_TEXT_COLOR;
        mSelectedTextColor = DEFAULT_TEXT_SELECT_COLOR;
        mLineColor = DEFAULT_LINE_COLOR;
        mUpItemCount = DEFAULT_ITEM_NUM_UP;
        mDownItemCount = DEFAULT_ITEM_NUM_DOWN;

        mDivideLineToTextSpacePx = DTPLUtil.dp2px(getContext(), INTERVAL_LINE_TO_TEXT);

        mPaint = new Paint();
        mPaint.setTextSize(mSelectedTextSizePx);
        mPaint.setColor(mTextColor);

        mMinViewWidth = DTPLUtil.getTextSize("0", mPaint).first + PADDINT_LEFTRIGHT;
        int textHeight = DTPLUtil.getTextSize("0", mPaint).second;
        mItemDistance = (int) (textHeight + mDivideLineToTextSpacePx * 4.0 / 3);
        mUpShowSpace = mUpItemCount * mItemDistance + textHeight + mDivideLineToTextSpacePx;
        mDownShowSpace = mUpItemCount * mItemDistance + mDivideLineToTextSpacePx;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
            if (mMaxTextWidth + PADDINT_LEFTRIGHT < mMinViewWidth) {
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMinViewWidth, MeasureSpec.AT_MOST);
            } else {
                widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxTextWidth + PADDINT_LEFTRIGHT, MeasureSpec.AT_MOST);
            }
        }
        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(mUpShowSpace + mDownShowSpace, MeasureSpec.AT_MOST);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mSelectedIndex <= 0) {
            initData();
        }
        requestLayout();
    }

    /**
     * 设置显示的数据
     *
     * @param list
     */
    public void setData(List<String> list) {
        mStrList = list;
        if (mCanInitData) {
            initData();
        }
    }

    /**
     * 初始化数据，将传入的String转换为条目对象
     */
    private void initData() {
        mCanInitData = true;
        if (mStrList == null || mStrList.size() == 0) {
            return;
        }
        mBaseLineY = getMeasuredHeight() / 2;
        mItemList.clear();
        mPaint.setTextSize(mTextSizePx);
        for (int i = 0; i < mStrList.size(); i++) {
            Pair<Integer, Integer> textSize = DTPLUtil.getTextSize(mStrList.get(i), mPaint);
            mMaxTextWidth = textSize.first > mMaxTextWidth ? textSize.first : mMaxTextWidth;
            if (i == 0) {
                mItemList.add(new ItemBean(mStrList.get(i), getMeasuredWidth() / 2 - textSize.first / 2, getMeasuredHeight() / 2,
                        mSelectedIndex == i ? mSelectedTextSizePx : mTextSizePx, mSelectedIndex == i ? mSelectedTextColor : mTextColor));
            } else {
                mItemList.add(new ItemBean(mStrList.get(i), getMeasuredWidth() / 2 - textSize.first / 2, mItemList.get(i - 1).getY() + mItemDistance,
                        mSelectedIndex == i ? mSelectedTextSizePx : mTextSizePx, mSelectedIndex == i ? mSelectedTextColor : mTextColor));
            }
        }
        updateData(0);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mPaint.setTextSize(mSelectedTextSizePx);
        mPaint.setColor(mLineColor);
        Pair<Integer, Integer> textSize = DTPLUtil.getTextSize("0", mPaint);
        canvas.drawLine(0, getMeasuredHeight() / 2 - textSize.second - mDivideLineToTextSpacePx, getMeasuredWidth(), getMeasuredHeight() / 2 - textSize.second - mDivideLineToTextSpacePx, mPaint);
        canvas.drawLine(0, getMeasuredHeight() / 2 + mDivideLineToTextSpacePx, getMeasuredWidth(), getMeasuredHeight() / 2 + mDivideLineToTextSpacePx, mPaint);
//        canvas.drawLine(0, getMeasuredHeight() / 2 - mUpShowSpace, getMeasuredWidth(), getMeasuredHeight() / 2 - mUpShowSpace, mPaint);
//        canvas.drawLine(0, getMeasuredHeight() / 2 + mDownShowSpace, getMeasuredWidth(), getMeasuredHeight() / 2 + mDownShowSpace, mPaint);
        for (int i = 0; i < mItemList.size(); i++) {
            mPaint.setTextSize((i == mSelectedIndex) ? mSelectedTextSizePx : mTextSizePx);
            mPaint.setColor((i == mSelectedIndex) ? mSelectedTextColor : mTextColor);
            mPaint.setAlpha(mItemList.get(i).getAlpha());
            canvas.drawText(mItemList.get(i).getStr(), mItemList.get(i).getX(), mItemList.get(i).getY(), mPaint);
        }
        if (mNeedSetSelectedIndex != -1) {
            setSelectedPosition(mNeedSetSelectedIndex);
            mNeedSetSelectedIndex = -1;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mItemList.size() < 1) {
            return true;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mTouchDownPoint = new PointF(event.getX(), event.getY());
                if (mScrollThread != null) {
                    mScrollThread.exit = true;
                }
                mLastY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                updateData(event.getY() - mLastY);
                mLastY = event.getY();

                VelocityTracker mVelocityTracker = VelocityTracker.obtain();
                mVelocityTracker.addMovement(event);
                mVelocityTracker.computeCurrentVelocity(SCROLL_INTERVAL_TIME);
                if (mVelocityTracker.getYVelocity() == 0) {
                    if (mSpeedRequestZero) {
                        mSpeed = 0;
                    } else {
                        mSpeedRequestZero = true;
                    }
                } else {
                    mSpeedRequestZero = false;
                    mSpeed = mVelocityTracker.getYVelocity();
                }
                break;
            case MotionEvent.ACTION_UP:
                if ((mSpeed == 0) && (Math.abs((mTouchDownPoint.x - event.getX())) < 3) && (Math.abs(mTouchDownPoint.y - event.getY()) < 3)) {
                    click(event.getY());
                } else {
                    mScrollThread = new ScrollThread(mSpeed);
                    mScrollThread.start();
                }
                break;
        }
        return true;
    }

    /**
     * 点击该y坐标对应的条目，将其跳转到选中状态
     *
     * @param y 判定点的Y坐标
     */
    private void click(float y) {
        float minDistance = Float.MAX_VALUE;
        float distance;
        int position = mSelectedIndex;
        for (int i = mSelectedIndex - mUpItemCount; i < mSelectedIndex + mDownItemCount + 1; i++) {
            if (i < 0 || i >= mItemList.size()) {
                continue;
            }
            distance = mItemList.get(i).getY() - y;
            if (Math.abs(distance) < minDistance) {
                position = i;
                minDistance = Math.abs(distance);
            } else {
                break;
            }
        }
        new AlignThread(position).start();
    }

    /**
     * 更新所有条目的信息
     *
     * @param scrollY 为正则整体向下滑动，为负则整体向上滑动
     */
    private synchronized void updateData(float scrollY) {
        if (mItemList.size() < 1) {
            return;
        }
        if (scrollY > 0) {
            if (mItemList.get(0).getY() + scrollY > mBaseLineY) {
                if (mScrollThread != null) {
                    mScrollThread.exit = true;
                }
                return;
            }
        } else {
            if (mItemList.get(mItemList.size() - 1).getY() + scrollY < mBaseLineY) {
                if (mScrollThread != null) {
                    mScrollThread.exit = true;
                }
                return;
            }
        }
        float minDistance = Float.MAX_VALUE;
        float distance;
        boolean hasFind = false;
        ItemBean itemBean;
        for (int i = 0; i < mItemList.size(); i++) {
            itemBean = mItemList.get(i);
            itemBean.setY(itemBean.getY() + scrollY);
            distance = mItemList.get(i).getY() - mBaseLineY;
            if (!hasFind) {
                if (Math.abs(distance) < minDistance) {
                    mSelectedIndex = i;
                    minDistance = Math.abs(distance);
                } else {
                    hasFind = true;
                }
            }
        }
        for (int i = mSelectedIndex - mUpItemCount; i < mSelectedIndex + mDownItemCount + 1; i++) {
            if (i < 0 || i >= mItemList.size()) {
                continue;
            }
            mItemList.get(i).setAlpha((int) (255F - 255F * mInterpolator.getInterpolation(Math.abs(mItemList.get(i).getY() - mBaseLineY) / mUpShowSpace)));
        }
        DTPLUtil.runOnUIThread(new Runnable() {
            @Override
            public void run() {
                invalidate();
            }
        });
    }

    /**
     * 设置当前选中的条目
     *
     * @param position 条目索引
     * @return true：设置成功；false：设置失败，列表中没有该索引
     */
    public boolean setSelectedPosition(int position) {
        if (position < 0) {
            return false;
        }
        if (position >= mItemList.size()) {
            mNeedSetSelectedIndex = position;
            return false;
        }
        mSelectedIndex = position;
        mLastSelectedIndex = mSelectedIndex;
        updateData(mBaseLineY - mItemList.get(mSelectedIndex).getY());
        return true;
    }

    /**
     * 获取当前选中条目的索引
     *
     * @return 当前选中条目的索引
     */
    public int getSelectedPosition() {
        return mSelectedIndex;
    }

    /**
     * 设置当前选中的条目
     *
     * @param str 条目内容
     * @return true：设置成功；false：设置失败，列表中没有该项
     */
    public boolean setSelected(String str) {
        if (TextUtils.isEmpty(str)) {
            return false;
        }
        int index = mStrList.indexOf(str);
        if (index >= 0) {
            setSelectedPosition(index);
        }
        return index >= 0;
    }

    /**
     * 获取当前选中的条目
     *
     * @return 当前选中的条目，若获取失败则返回null
     */
    public String getSelected() {
        if (mItemList.size() < (mSelectedIndex + 1)) {
            return null;
        }
        return mItemList.get(mSelectedIndex).getStr();
    }

    /**
     * 设置未选中的文字颜色
     *
     * @param color
     */
    public void setNomalColor(int color) {
        mTextColor = color;
    }

    /**
     * 设置选中的文字颜色
     *
     * @param color
     */
    public void setSelectedColor(int color) {
        mSelectedTextColor = color;
    }

    /**
     * 设置分隔线颜色
     *
     * @param color
     */
    public void setLineColor(int color) {
        mLineColor = color;
    }

    /**
     * 手指离开屏幕后的滑动动作，逐渐减速滑动一直到速度为0
     */
    private class ScrollThread extends Thread {
        private static final int DEFAULT_SCROLL_ACCEL = 10; // 每个时间单位速度降低量
        private float speed;
        private float accel;
        public boolean exit = false;

        public ScrollThread(float speed) {
            this.speed = speed;
            this.accel = ((speed > 0) ? -1 : 1) * DEFAULT_SCROLL_ACCEL;
        }

        @Override
        public void run() {
            while (!exit) {
                updateData(speed / 1000);
                if ((speed == 0) || (speed + accel) / speed < 0) {
                    exit = true;
                    break;
                }
                speed += accel;
                SystemClock.sleep(SCROLL_INTERVAL_TIME / 1000);
            }
            new AlignThread().start();
        }
    }

    /**
     * 将条目滑动对齐到BaseLine
     */
    private class AlignThread extends Thread {
        public boolean exit = false;
        private boolean hasDirection;
        private boolean directionUp;
        private int position;

        /**
         * 将距离BaseLine最近的条目滑动对齐到BaseLine
         */
        public AlignThread() {
        }

        /**
         * 将指定的条目滑动对齐到BaseLine
         *
         * @param position 要对齐的条目索引
         */
        public AlignThread(int position) {
            this.position = position;
            directionUp = (mItemList.get(position).getY() - mBaseLineY) > 0;
            hasDirection = true;
        }

        @Override
        public void run() {
            if (mItemList.size() < 1) {
                return;
            }
            int speed = 50;
            if (!hasDirection) {
                float minDistance = Float.MAX_VALUE;
                float distance;
                for (int i = 0; i < mItemList.size(); i++) {
                    distance = mItemList.get(i).getY() - mBaseLineY;
                    if (Math.abs(distance) < minDistance) {
                        mSelectedIndex = i;
                        directionUp = distance > 0;
                        minDistance = Math.abs(distance);
                    } else {
                        break;
                    }
                }
            }
            while (!exit) {
                if (hasDirection && (mSelectedIndex != position)) {
                    updateData((directionUp ? -1 : 1) * speed);
                } else if (Math.abs((mItemList.get(mSelectedIndex).getY() - mBaseLineY)) < speed) {
                    updateData(-1 * (mItemList.get(mSelectedIndex).getY() - mBaseLineY));
                    exit = true;
                } else {
                    updateData((directionUp ? -1 : 1) * speed);
                }
                SystemClock.sleep(20);
            }
            mSpeed = 0;
            if ((mLastSelectedIndex != mSelectedIndex) && (mOnItemChangedListener != null)) {
                mLastSelectedIndex = mSelectedIndex;
                mOnItemChangedListener.onItemChanged(WheelView.this, mSelectedIndex);
            }
        }
    }

    /**
     * 设置条目改变时的回调监听器
     *
     * @param onItemChangedListener
     */
    public void setOnItemChangedListener(OnItemChangedListener onItemChangedListener) {
        mOnItemChangedListener = onItemChangedListener;
    }

    /**
     * 当选中条目改变时的回调
     */
    public interface OnItemChangedListener {
        public void onItemChanged(WheelView wheelView, int position);
    }

}
